package org.music.generate;

import com.google.common.collect.Maps;
import com.mdc.mansion.common.util.DateUtils;
import com.mdc.mansion.common.util.FreeMarkers;
import freemarker.template.Configuration;
import freemarker.template.Template;
import org.apache.commons.dbcp.BasicDataSource;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.music.generate.model.ColumnMetaData;
import org.music.generate.model.GenerateParamData;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.support.rowset.SqlRowSet;
import org.springframework.jdbc.support.rowset.SqlRowSetMetaData;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

/**
 * 代码生成器
 * @author chenmingdong
 *
 * 2016年2月9日
 */
public class GenerateCode {
	private final static Logger logger = LoggerFactory.getLogger(GenerateCode.class);
	private final static String packageName = "com.mdc.mansion";
	private final static String projectName = "mansion";

	public static void main(String[] args) throws Exception {
		// ========== ↓↓↓↓↓↓ 执行前请修改参数，谨慎执行。↓↓↓↓↓↓ ====================

		// 主要提供基本功能模块代码生成。
		JdbcTemplate srcJdbcTemplate = getJdbcTemplate("com.mysql.jdbc.Driver","jdbc:mysql://39.106.205.177:3306/mansion?characterEncoding=UTF-8","mansion","bexAL82ceB7R8Rxe");

		// 模块名
		String moduleName = "pay";
		// 类作者，例：chenmd
		String classAuthor = "xizf";
		// 类名，例：user
//		String className = "RestaurantPayCfg";
		//该类对应的表名称
//		String tableName="restaurant_pay_cfg";
		// 功能名，例：用户（简单的功能描述）
		String functionName = "在线支付配置";
		// 实例id
		String entityId = "id";
		// 表主键id
		String tableId = "id";
		Boolean isEnable = true;
		
		Map<String,String> tableMap = new HashMap<String,String>();///// key:tableName  VALUE：className
//		tableMap.put("restaurant_pay_cfg","RestaurantPayCfg");
//		tableMap.put("restaurant_order_pay","RestaurantOrderPay");
//		tableMap.put("restaurant_order_pay_back","RestaurantOrderPayBack");
//		tableMap.put("restaurant_order_pay_refund","RestaurantOrderPayRefund");
		tableMap.put("restaurant_pay_cfg","RestaurantPayCfg");
		
		for(Map.Entry<String, String> entry : tableMap.entrySet())
		{
			List<ColumnMetaData> columneMetaList = getColumnMetaDatas(srcJdbcTemplate,entry.getKey());
			boolean isGenerateService=true;
			boolean isGenerateController=true;
			boolean isGenerateView=true;
			// ========== ↑↑↑↑↑↑ 执行前请修改参数，谨慎执行。↑↑↑↑↑↑ ====================
			GenerateParamData generateParamData=new GenerateParamData(columneMetaList, moduleName,classAuthor,entry.getValue(),entry.getKey(),
					functionName, isEnable, entityId,tableId, isGenerateService,isGenerateController,isGenerateView);
			generateCode(generateParamData);
		}
	}

	private static void generateCode(GenerateParamData generateParamData) throws IOException {
		if (!generateParamData.getEnable()){
			logger.error("请启用代码生成工具，设置参数：isEnable = true");
			return;
		}

		if (StringUtils.isBlank(generateParamData.getModuleName()) || StringUtils.isBlank(generateParamData.getModuleName())
				|| StringUtils.isBlank(generateParamData.getClassName()) || StringUtils.isBlank(generateParamData.getFunctionName())){
			logger.error("参数设置错误：包名、模块名、类名、功能名不能为空。");
			return;
		}

		// 获取文件分隔符
		String separator = File.separator;

		// 获取工程路径
		File projectPath = new DefaultResourceLoader().getResource("").getFile();
		while(!projectPath.getPath().endsWith(projectName)){
			projectPath = projectPath.getParentFile();
		}
		logger.info("Project Path: {}", projectPath);

		// 模板文件路径
		String tplPath = StringUtils.replace(projectPath+"/mansion-component/mansion-component-generate/src/main/java/org/music/generate/template/admin", "/", separator);
		logger.info("Template Path: {}", tplPath);

		// 代码模板配置
		Configuration cfg = new Configuration();
		cfg.setDefaultEncoding("UTF-8");
		cfg.setDirectoryForTemplateLoading(new File(tplPath));

		// 定义模板变量
		Map<String, Object> model = createModel(generateParamData);

		String pPath=projectPath.getPath();
		generateEntity(generateParamData.getModuleName(), separator,pPath, cfg, model);
		generateDao(generateParamData.getModuleName(), separator, cfg, model, pPath);
		generateMapper(generateParamData.getModuleName(), separator,pPath, cfg, model);

		if(generateParamData.isGenerateService()){
			generateService(generateParamData.getModuleName(), separator,pPath, cfg, model);
			generateServiceImpl(generateParamData.getModuleName(), separator, cfg, model, pPath);
		}

		if(generateParamData.isGenerateController()){
			generateController(generateParamData.getModuleName(), separator, pPath, cfg, model);
		}

		if(generateParamData.isGenerateView()){
			generateView(separator, pPath, cfg, model);
			generateViewList(separator, pPath, cfg, model);
		}
		logger.info("Generate Success.");
	}

	private static Map<String, Object> createModel(GenerateParamData generateParamData) {
		Map<String, Object> model = Maps.newHashMap();
		model.put("packageName", StringUtils.lowerCase(packageName));
		model.put("moduleName", StringUtils.lowerCase(generateParamData.getModuleName()));
		model.put("baseModuleName", StringUtils.capitalize(generateParamData.getModuleName()));
		model.put("className", StringUtils.uncapitalize(generateParamData.getClassName()));
		model.put("ClassName", StringUtils.capitalize(generateParamData.getClassName()));
		model.put("classAuthor", StringUtils.isNotBlank(generateParamData.getClassAuthor())?generateParamData.getClassAuthor():"Generate Tools");
		model.put("classVersion", DateUtils.getStandardNowDate());
		model.put("functionName", generateParamData.getFunctionName());
		model.put("urlPrefix", model.get("moduleName")+"/"+model.get("className"));
		model.put("viewPrefix", model.get("urlPrefix"));
		model.put("permissionPrefix", StringUtils.lowerCase(generateParamData.getModuleName())+":"+StringUtils.uncapitalize(generateParamData.getClassName()));
		model.put("sqlId", generateParamData.getModuleName()+generateParamData.getClassName()+"Columns");
		model.put("tableName", generateParamData.getTableName());
		model.put("entityId", generateParamData.getEntityId());
		model.put("tableId", generateParamData.getTableId());
		model.put("columns", generateParamData.getColumneMetaList());
		model.put("columnMetaDatas", generateParamData.getTableMetaData().getColumnMetaDatas());
		model.put("imports", generateParamData.getTableMetaData().getImports());
		return model;
	}

	private static List<ColumnMetaData> getColumnMetaDatas(JdbcTemplate srcJdbcTemplate,String tableName) {
		SqlRowSet sqlRowSet = srcJdbcTemplate.queryForRowSet("SELECT * FROM "+tableName+" LIMIT 0"); //注意limit 0更合适
		List<ColumnMetaData> columneMetaList = new LinkedList<ColumnMetaData>();
		int columnCount;
		SqlRowSetMetaData sqlRowSetMetaData = sqlRowSet.getMetaData();
		columnCount = sqlRowSetMetaData.getColumnCount();
		for (int i = 1; i <= columnCount; i++) {
            columneMetaList.add(new ColumnMetaData(sqlRowSetMetaData.getColumnName(i), sqlRowSetMetaData.getColumnType(i), sqlRowSetMetaData.getColumnTypeName(i))); //获取字段的名称、类型和描述
        }
		return columneMetaList;
	}

	private static JdbcTemplate getJdbcTemplate(String className,String url,String userName,String password) {
		JdbcTemplate srcJdbcTemplate=new JdbcTemplate();
		BasicDataSource ds=new BasicDataSource();
		ds.setDriverClassName(className);
		ds.setUrl(url);
		ds.setUsername(userName);
		ds.setPassword(password);
		srcJdbcTemplate.setDataSource(ds);
		return srcJdbcTemplate;
	}


	private static void generateViewList(String separator, String projectPath,
			Configuration cfg, Map<String, Object> model) throws IOException {
		Template template;
		String content;
		//view文件路径
		String viewPath = StringUtils.replaceEach(projectPath+"/mansion-web/src/main/webapp/WEB-INF/views/modules",
				new String[]{"/", "."}, new String[]{separator, separator});
		logger.info("view Java Path: {}", viewPath);
		//生成view
		template = cfg.getTemplate("viewlist.ftl");
		content = FreeMarkers.renderTemplate(template, model);
		String viewFilePath =viewPath+separator+model.get("moduleName")+separator+model.get("className")+"List.jsp"; 
		writeFile(content, viewFilePath);
		logger.info("viewList  generate success: {}", viewFilePath);
	}
	private static void generateView(String separator, String projectPath,
			Configuration cfg, Map<String, Object> model) throws IOException {
		Template template;
		String content;
		//view文件路径
		String viewPath = StringUtils.replaceEach(projectPath+"/mansion-web/src/main/webapp/WEB-INF/views/modules",
				new String[]{"/", "."}, new String[]{separator, separator});
		logger.info("view Java Path: {}", viewPath);
		//生成view
		template = cfg.getTemplate("view.ftl");
		content = FreeMarkers.renderTemplate(template, model);
		String viewFilePath =viewPath+separator+model.get("moduleName")+separator+model.get("className")+"Form.jsp";
		writeFile(content, viewFilePath);
		logger.info("view  generate success: {}", viewFilePath);
	}
	private static void generateController(String moduleName, String separator,
			String projectPath, Configuration cfg, Map<String, Object> model)
			throws IOException {
		Template template;
		String content;
		//controller文件路径
		String controllerJavaPath = StringUtils.replaceEach(projectPath+"/mansion-web/src/main/java/"+StringUtils.lowerCase(packageName),
						new String[]{"/", "."}, new String[]{separator, separator});
		logger.info("Controller Java Path: {}", controllerJavaPath);
				
				
		//生成controller
		template = cfg.getTemplate("controller.ftl");
		content = FreeMarkers.renderTemplate(template, model);
		String controllerFilePath = controllerJavaPath+separator+"web"+separator+"modules"+separator+moduleName+separator+"web"+separator+model.get("ClassName")+"Controller.java";
		writeFile(content, controllerFilePath);
		logger.info("controller generate success: {}", controllerFilePath);
	}

	private static void generateServiceImpl(String moduleName,
			String separator, Configuration cfg, Map<String, Object> model,
			String projectPath) throws IOException {
		Template template;
		String content;
		//生成service impl接口
		String serviceJavaPath = StringUtils.replaceEach(projectPath+"/mansion-core/src/main/java/"+StringUtils.lowerCase(packageName),
				new String[]{"/", "."}, new String[]{separator, separator});
		template = cfg.getTemplate("serviceimpl.ftl");
		content = FreeMarkers.renderTemplate(template, model);
		String serviceImplFilePath = serviceJavaPath+separator+"core"+separator+"modules"+separator+moduleName+separator+"impl"+separator+model.get("ClassName")+"ServiceImpl.java";
		writeFile(content, serviceImplFilePath);
		logger.info("serviceImpl generate success: {}", serviceImplFilePath);
	}

	private static void generateService(String moduleName, String separator,
			String projectPath, Configuration cfg, Map<String, Object> model)
			throws IOException {
		Template template;
		String content;
		// service文件路径
		String serviceJavaPath = StringUtils.replaceEach(projectPath+"/mansion-core/src/main/java/"+StringUtils.lowerCase(packageName),
						new String[]{"/", "."}, new String[]{separator, separator});
		logger.info("Service Java Path: {}", serviceJavaPath);
		//生成service接口
		template = cfg.getTemplate("service.ftl");
		content = FreeMarkers.renderTemplate(template, model);
		String serviceFilePath = serviceJavaPath+separator+"core"+separator+"modules"+separator+moduleName+separator+model.get("ClassName")+"Service.java";
		writeFile(content, serviceFilePath);
		logger.info("service generate success: {}", serviceFilePath);
	}
	
	private static void generateMapper(String moduleName, String separator,
			String projectPath, Configuration cfg, Map<String, Object> model)
			throws IOException {
		Template template;
		String content;
		String mapperPath = StringUtils.replaceEach(projectPath+"/mansion-dataaccess/src/main/resources/mappings/modules",
						new String[]{"/", "."}, new String[]{separator, separator});
		logger.info("mapper Java Path: {}", mapperPath);
		//生成domain 持久层
		template = cfg.getTemplate("mapper.ftl");
		content = FreeMarkers.renderTemplate(template, model);
		String mapperFilePath = mapperPath+separator+moduleName+separator+model.get("ClassName")+"Dao.xml";
		writeFile(content, mapperFilePath);
		logger.info("mapper generate success: {}", mapperFilePath);
	}

	private static void generateDao(String moduleName, String separator,
			Configuration cfg, Map<String, Object> model, String projectPath)
			throws IOException {
		Template template;
		String content;
		//生成dao 持久层
		String domainJavaPath=StringUtils.replaceEach(projectPath+"/mansion-dataaccess/src/main/java/"+StringUtils.lowerCase(packageName),
				new String[]{"/", "."}, new String[]{separator, separator});
		template = cfg.getTemplate("dao.ftl");
		content = FreeMarkers.renderTemplate(template, model);
		String repositoryFilePath = domainJavaPath+separator+"dataaccess"+separator+"modules"+separator+moduleName+separator+"dao"+separator+model.get("ClassName")+"Dao.java";
		writeFile(content, repositoryFilePath);
		logger.info("dao generate success: {}", repositoryFilePath);
	}

	private static void generateEntity(String moduleName, String separator,
			String projectPath, Configuration cfg, Map<String, Object> model)
			throws IOException {
		Template template;
		String content;
		String entityPath = StringUtils.replaceEach(projectPath+"/mansion-dataaccess/src/main/java/"+StringUtils.lowerCase(packageName),
						new String[]{"/", "."}, new String[]{separator, separator});
		logger.info("entity dao Java Path: {}", entityPath);
		//生成domain 持久层
		template = cfg.getTemplate("entity.ftl");
		content = FreeMarkers.renderTemplate(template, model);
		String entityFilePath = entityPath+separator+"dataaccess"+separator+"modules"+separator+moduleName+separator+"entity"+separator+model.get("ClassName")+".java";
		writeFile(content, entityFilePath);
		logger.info("entity generate success: {}", entityFilePath);
	}
	
	/**
	 * 将内容写入文件
	 * @param content
	 * @param filePath
	 */
	public static void writeFile(String content, String filePath) {
		File file=new File(filePath);
		if(file.exists()){
			logger.info("生成失败，文件已存在！");
		}else{
			try {
				file.getParentFile().mkdirs();
				IOUtils.write(content, new FileOutputStream(file), "UTF-8");
			} catch (FileNotFoundException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}


